/**********************************************************************/
/**  Copyright (c) ECC labs. 2001-2002. All Rights Reserved.         **/
/**********************************************************************/

/**********************************************************************
	some functions on families info & tensor products sums  etc.
**********************************************************************/

#include "binadd.h"


/////// my change /////////////////

#ifdef LINUX
int randomize() {
	ifstream rnd ("/dev/random") ;
	typedef union {
		unsigned long rint ;
		char rch[4] ;
	} rnd_t ;
	rnd_t r ;
	rnd.get(r.rch, 4) ;
	srand(r.rint) ;
	rnd.close() ;
	return 0 ;
}
#endif /* LINUX */
/////// end my change /////////////////

CBinWord indep (CBinCode& C) {
		C.row2col() ;
		CBinWord ldep(C.k) ;
		ldep.num = C.step_col() ;
		C.k = ldep.wt() ;

		for ( int i=0, j=0; i<C.k; i++,j++ ) {
			for(; ! ldep.is_set(j) ; j++) ;
			C.row[i] = C.row[j] ;
		}

		return ldep ;
} ;

/*******************************
		Codes family reader
		Reads a family ( or 2 families ) of codes from a file
		* on begining of string is codes separator
		/ on begining of string is families separator
		last string MUST begin with *
*******************************/
int filter( char *filename, CBinCode* C, CBinCode* D ) {


		t_code_family fam ;
	ifstream in(filename) ;
	//in.eatwhite() ;
	if(!in) {
		cerr << "File not exist : " << filename ;
		return -1 ;
	}

	char tstbuf[MAX_STR_LEN] ;
	int tstlen=0 ;
	int str_cnt=0, k_cnt=0 ;
	fam.all = 0 ;
	fam.sec = MAX_FAM_MEMB ;

	/////////// Filtering file strings //////////////////
	while ( !in.eof() ) {
		in.getline(tstbuf, MAX_STR_LEN) ;
		tstlen = in.gcount()-1;
		if  ( tstbuf[0] == '*' ) {
			fam.k[fam.all] = k_cnt ;
			k_cnt = 0 ;
			fam.all++ ;
		}
		else if ( tstbuf[0] == '/' ){
			fam.k[fam.all] = k_cnt ;
			k_cnt = 0 ;
			fam.sec = fam.all+++1 ;
		}
		else {
			if ( k_cnt == 0 )
				fam.n[fam.all] = tstlen ;
			k_cnt++ ;
			str_cnt++ ;
		}
	}
	if  ( tstbuf[0] != '*' ) {
			fam.k[fam.all] = k_cnt ;
			k_cnt = 0 ;
			fam.all++ ;
		}

	if(fam.sec > fam.all) fam.sec = fam.all ;
	/////////// End filtering ///////////////////////////

	/////////// Creating code families //////////////////

	C = new CBinCode[fam.sec] ;
	if ( D != NULL )
		D = new CBinCode[fam.all - fam.sec] ;
	in.seekg(0, in.beg) ;
	in.clear() ;

	for (int i=0; i<fam.sec; i++) {
		C[i].k = fam.k[i] ;
		C[i].n = fam.n[i] ;
		for (int j=0; j<fam.k[i]; j++) C[i].row[j].n = fam.n[i] ;
		for (int j=0; j<fam.k[i]; j++ ) {
			do {
				in.getline(tstbuf, MAX_STR_LEN) ;
				tstbuf[in.gcount()-1] = '\0' ;
			} while ( (tstbuf[0]=='*') || (tstbuf[0]=='/') ) ;
			C[i].row[j].num = C[i].row[j].bin2dec(tstbuf) ;
		}
	}
	for (int i=fam.sec; i<fam.all; i++) {
		D[i-fam.sec].k = fam.k[i] ;
		D[i-fam.sec].n = fam.n[i] ;
		for (int j=0; j<fam.k[i]; j++)
			D[i-fam.sec].row[j].n = fam.n[i] ;
		for (int j=0; j<fam.k[i]; j++ )  {
			do {
				in.getline(tstbuf, MAX_STR_LEN) ;
				tstbuf[in.gcount()-1] = '\0' ;
			} while ( (tstbuf[0]=='*') || (tstbuf[0]=='/') ) ;
			D[i-fam.sec].row[j].num = D[i-fam.sec].row[j].bin2dec(tstbuf) ;
		}
	}
	return fam.sec ;
} ;

/*
	Family's configuration data calc
	all intrsections & sums dimntions & weights
	will fill in t_conf_data type variable rs
*/

int conf_calc ( CBinCode *C, t_conf_data *rs, int s ) {
	CBinCode A(0, C[0].n) ;

	for(int i=1; i < (1<<s); i++){
		for ( int j = 0 ; (j < s) && ( A.k < A.n ) ; j++ )
			if ( (i>>j) & 1 )
				A&=C[j] ;
		rs[i].sum_k = A.k ;
		rs[i].sum_d = ( A.k == A.n ) ? 1 : A.d1() ;
		A.k = 0 ;
	}

	for(int i=0; i < s; i++ ) C[i].convert() ;
	A.k = 0 ;
	A.flag^=IS_H^IS_G ;

	for(int i=1; i < ( 1 << s ) ; i++){
		for ( int j=0; ( j < s ) && ( A.k < A.n ); j++ )
			if ( (i>>j) & 1 )
				A&=C[j] ;
		rs[i].its_k = A.n - A.k ;
		A.d2() ;
		rs[i].its_d = ( A.k == A.n ) ? 0 : A.d ;
		A.k = 0 ;
	}

	return 0 ;
}

// Tensor product's sum dimention calc

int tp_dim ( t_conf_data *rs_C, t_conf_data *rs_D, int s ) {
	CBinWord cnt ;
	int dim = 0 ;
	for ( cnt.num=1; cnt.num < ( 1 << s ); cnt.num++ ) {
		int sgn = ( cnt.wt() % 2 ) ? 1 : -1 ;
		dim += sgn * rs_C[cnt.num].its_k * rs_D[cnt.num].its_k ;
	}
	return dim ;
}

// Tensor product's sum distance upper bound calc

int tp_bound ( t_conf_data *rs_C, t_conf_data *rs_D, int s ) {
	CBinWord cnt ;
	unsigned int bound = 50000 ;
	int d1, d2 ;
	for ( cnt.num = 1; cnt.num < ( 1 << s ); cnt.num++ ) {
		d1 = rs_C[cnt.num].its_d * rs_D[cnt.num].sum_d ;
		d2 = rs_C[cnt.num].sum_d * rs_D[cnt.num].its_d ;
		if ( (d1 < d2) && (d1 != 0)) d2 = d1 ;
		if (d2 < bound && d2 != 0) bound = d2 ;
	}
	return bound ;

}

///////// Tensor products sum calculation ///////////

CBinCode& tp (CBinCode* C, CBinCode* D, int s) {

	for ( int i=0; i<s; i++ ) C[i] = C[i]^D[i] ;
	for ( int i=1; i<s; i++ ) C[0] &= C[i] ;
	return C[0] ;
}

///////// Tensor products sum calculation ///////////

CBinCode tpsum(CBinCode *C, CBinCode *D, int s) {
	    int tlen = C[0].n * D[0].n ,
        tdim=0 ;
    for(int i=0; i<s; i++) tdim += C[i].k * D[i].k ;
    CBinCode T(tdim, tlen) ;
    int k=0 ;

		for(int r=0; r<s; r++) {
        for(int i=0; i<C[r].k; i++) {
            for(int j=0; j<D[r].k; j++) {
                T.row[k] = C[r].row[i] ^ D[r].row[j] ;
                k++ ;
            }
        }
    }
		return T ;
}

////////// My ? Hausse ? ///////////////////
CBinWord CBinCode::step(){
    long main_var = 0 ;

    for( t_cnt i=0; i<k; i++ ){
        for( t_cnt j=i; j<n; ){
            if( row[i].is_set(j) ){
                main_var|=(long)1<<j ;
                for( t_cnt t=i+1; t<k; t++ ){
                    if( row[t].is_set(j) )
                        row[t].num^=row[i].num ;
                }
                break ;
            }
            else{
                t_cnt t=i+1;
                for( ; t<k; t++ ){
                    if( row[t].is_set(j) ){
                        row[i].num^=row[t].num ;
                        main_var|=(long)1<<j ;
                        break ;
                    }
                }
                if( t==k ) j++ ;
            }
        }
    }
		CBinWord res ;
    res.num = main_var ;
return res ;
};

t_src CBinCode::step_col(){
    t_src main_var = 0 ;

    for( t_cnt i=0; i<n; i++ ){
        for( t_cnt j=i; j<k; ){
            if( col[i].is_set(j) ){
                main_var|=(long)1<<j ;
                for( t_cnt t=i+1; t<n; t++ ){
                    if( col[t].is_set(j) )
                        col[t].num^=col[i].num ;
                }
                break ;
            }
            else{
                t_cnt t=i+1;
                for( ; t<n; t++ ){
                    if( col[t].is_set(j) ){
                        col[i].num^=col[t].num ;
                        main_var|=(long)1<<j ;
                        break ;
                    }
                }
                if( t==n ) j++ ;
            }
        }
    }
		// CBinWord res ;
    // res.num = main_var ;
return main_var ;
};


/////// gets code nfo from a file //////
CBinCode& code_init(char *fname) {
	char tstbuf[MAX_STR_LEN] ;
	ifstream in (fname) ;

	////////// k & n calc ////////////
	in.getline(tstbuf, MAX_STR_LEN) ;
	int n = in.gcount() - 1, k = 0 ;
	for (;!in.eof(); k++ ) {
		in.getline(tstbuf, MAX_STR_LEN) ;
	}
	in.close() ;

	///////// init ///////////////////
	ifstream in1(fname) ;
	CBinCode *R ;
	R = new CBinCode(k, n) ;

	in1 >> *R ;

	return *R ;
};

/////////// sorts code's G matrix (increasing wights) ////////
void sort_(CBinCode& C) {
	int min_wt = C.row[0].wt(),
		min_j = 0 ;
	t_src tmp_buf ;

	for ( int i = 0; i<C.k; i++ ) {
		for ( int j=i+1; j<C.k; j++) {
			int cur_wt = C.row[j].wt() ;
			if ( min_wt > cur_wt ) {
				min_wt = cur_wt ;
				min_j = j ;
			}
		}
		if(min_j > i) {
			tmp_buf = C.row[i].num ;
			C.row[i].num = C.row[min_j].num ;
			C.row[min_j].num = tmp_buf ;
			min_wt = C.row[i+1].wt() ;
		}
		min_j = i + 1 ;
	}

}

